/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.openide.explorer.view; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.beans.PropertyChangeListener; import java.util.*; import javax.swing.UIManager; import javax.swing.SwingUtilities; import javax.swing.event.EventListenerList; import javax.swing.tree.TreeNode; import org.openide.nodes.*; import org.openide.util.Mutex; import org.openide.util.WeakSet; import org.openide.util.WeakListener; /** Visual representation of one node. Holds necessary information about nodes * like icon, name, description and also list of its children. * <P> * There is at most one VisualizerNode for one node. All of them are hold in a cache. * <P> * The VisualizerNode level provides secure layer between Nodes and Swing AWT dispatch * thread. * * @author Jaroslav Tulach */ final class VisualizerNode extends EventListenerList implements NodeListener, TreeNode, Runnable { /** one template to use for searching for visualizers */ private static final VisualizerNode TEMPLATE = new VisualizerNode (0); /** constant holding empty reference to children */ private static final Reference NO_REF = new WeakReference (null); /** cache of visializers (VisualizerNode, Reference (VisualizerNode)) */ private static WeakHashMap cache = new WeakHashMap (); /** empty visualizer */ public static final VisualizerNode EMPTY = getVisualizer (null, Node.EMPTY); /** Finds VisualizerNode for given node. Can be called only from EventQueue. * @param ch the children this visualizer should belong to * @param n the node * @return the visualizer */ public static VisualizerNode getVisualizer (VisualizerChildren ch, Node n) { TEMPLATE.hashCode = System.identityHashCode (n); Reference r = (Reference)cache.get (TEMPLATE); VisualizerNode v = r == null ? null : (VisualizerNode)r.get (); if (v == null) { v = new VisualizerNode (n); cache.put (v, new WeakReference (v)); } if (ch != null) { v.parent = ch; } return v; } /** node */ final Node node; /** system hashcode of the node */ private int hashCode; /** visualizer children attached thru weak references Reference (VisualizerChildren) */ private Reference children = NO_REF; /** the VisualizerChildren that contains this VisualizerNode or null */ private VisualizerChildren parent; /** listener on UI */ private PropertyChangeListener uiListener; /** cached name */ public String name; /** cached display name */ public String displayName; /** cached short description */ public String shortDescription; /** renderer tree or null */ private NodeRenderer.Tree rendererTree; /** renderer list or null */ private NodeRenderer.List rendererList; /** renderer listpane or null */ private NodeRenderer.Pane rendererPane; static final long serialVersionUID =3726728244698316872L; /** Constructor that creates template for the node. */ private VisualizerNode (int hashCode) { this.hashCode = hashCode; this.node = null; } /** Creates new VisualizerNode * @param n node to refer to */ private VisualizerNode(Node n) { node = n; hashCode = System.identityHashCode (node); // attach as a listener node.addNodeListener (WeakListener.node (this, node)); uiListener = WeakListener.propertyChange (this, null); UIManager.addPropertyChangeListener (uiListener); name = node.getName (); displayName = node.getDisplayName (); shortDescription = node.getShortDescription (); } /** When finalized remove the ui listener */ protected void finalize () { UIManager.removePropertyChangeListener (uiListener); } /** Getter for list of children of this visualizer. * @return list of VisualizerNode objects */ public List getChildren () { VisualizerChildren ch = (VisualizerChildren)children.get (); if (ch == null && !node.isLeaf ()) { // go into lock to ensure that no childrenAdded, childrenRemoved, // childrenReordered notifications occures and that is why we do // not loose any changes ch = (VisualizerChildren)Children.MUTEX.readAccess (new Mutex.Action () { public Object run () { Node[] nodes = node.getChildren ().getNodes (); VisualizerChildren vc = new VisualizerChildren ( VisualizerNode.this, nodes ); notifyVisualizerChildrenChange (nodes.length, vc); return vc; } }); } return ch == null ? Collections.EMPTY_LIST : ch.list; } // // TreeNode interface (callable only from AWT-Event-Queue) // public int getIndex(final javax.swing.tree.TreeNode p1) { return getChildren ().indexOf (p1); } public boolean getAllowsChildren() { return !isLeaf (); } public javax.swing.tree.TreeNode getChildAt(int p1) { return (VisualizerNode)getChildren ().get (p1); } public int getChildCount() { return getChildren ().size (); } public java.util.Enumeration children() { return java.util.Collections.enumeration (getChildren ()); } public boolean isLeaf() { return node.isLeaf (); } public javax.swing.tree.TreeNode getParent() { Node parent = node.getParentNode (); return parent == null ? null : getVisualizer (null, parent); } // *************** // Rendering stuff (only from AWT-QUEUE) // *************** /** Tree renderer for this node. */ public NodeRenderer.Tree getTree () { if (rendererTree == null) { rendererTree = new NodeRenderer.Tree (); rendererTree.update (this); } return rendererTree; } /** List renderer for this node. */ public NodeRenderer.List getList () { if (rendererList == null) { rendererList = new NodeRenderer.List (); rendererList.update (this); } return rendererList; } /** Pane renderer for this node. */ public NodeRenderer.Pane getPane () { if (rendererPane == null) { rendererPane = new NodeRenderer.Pane (); rendererPane.update (this); } return rendererPane; } // ********************************************** // Can be called under Children.MUTEX.writeAccess // ********************************************** /** Fired when a set of new children is added. * @param ev event describing the action */ public void childrenAdded(NodeMemberEvent ev) { VisualizerChildren ch = (VisualizerChildren)children.get (); if (ch == null) return; SwingUtilities.invokeLater (new VisualizerEvent.Added ( ch, ev.getDelta (), ev.getDeltaIndices () )); } /** Fired when a set of children is removed. * @param ev event describing the action */ public void childrenRemoved(NodeMemberEvent ev) { VisualizerChildren ch = (VisualizerChildren)children.get (); if (ch == null) return; SwingUtilities.invokeLater (new VisualizerEvent.Removed ( ch, ev.getDeltaIndices () )); } /** Fired when the order of children is changed. * @param ev event describing the change */ public void childrenReordered(NodeReorderEvent ev) { VisualizerChildren ch = (VisualizerChildren)children.get (); if (ch == null) return; SwingUtilities.invokeLater (new VisualizerEvent.Reordered ( ch, ev.getPermutation () )); } /** Fired when the node is deleted. * @param ev event describing the node */ public void nodeDestroyed(NodeEvent ev) { // ignore for now } /** Change in the node properties (icon, etc.) */ public void propertyChange(final java.beans.PropertyChangeEvent evt) { String name = evt.getPropertyName (); if ( Node.PROP_NAME.equals (name) || Node.PROP_DISPLAY_NAME.equals (name) || Node.PROP_SHORT_DESCRIPTION.equals (name) || Node.PROP_ICON.equals (name) || Node.PROP_OPENED_ICON.equals (name) ) { SwingUtilities.invokeLater (this); return; } if ( "lookAndFeel".equals (name) // NOI18N ) { rendererTree = null; rendererList = null; rendererPane = null; SwingUtilities.invokeLater (this); } } /** Update the state of this class by retrieving new name, etc. * And fire change to all listeners. Only by AWT-Event-Queue */ public void run () { name = node.getName (); displayName = node.getDisplayName (); shortDescription = node.getShortDescription (); // // update renderers // NodeRenderer.Tree t = rendererTree; if (t != null) { t.update (this); } NodeRenderer.List l = rendererList; if (l != null) { l.update (this); } NodeRenderer.Pane p = rendererPane; if (p != null) { p.update (this); } // // notify models // VisualizerNode parent = this; while (parent != null) { Object[] listeners = parent.getListenerList (); for (int i = listeners.length - 1; i >= 0; i -= 2) { ((NodeModel)listeners[i]).update (this); } parent = (VisualizerNode)parent.getParent (); } } // // Access to VisualizerChildren // /** Notifies change in the amount of children. This is used to distinguish between * weak and hard reference. Called from VisualizerChildren * @param size amount of children * @param ch the children */ void notifyVisualizerChildrenChange (int size, VisualizerChildren ch) { if (size == 0) { // hold the children hard children = new StrongReference (ch); } else { children = new WeakReference (ch); } } // ******************************** // This can be called from anywhere // ******************************** /** Adds visualizer listener. */ public synchronized void addNodeModel (NodeModel l) { add (NodeModel.class, l); } /** Removes visualizer listener. */ public synchronized void removeNodeModel (NodeModel l) { remove (NodeModel.class, l); } /** Hash code */ public int hashCode () { return hashCode; } /** Equals two objects are equal if they have the same hash code */ public boolean equals (Object o) { if (!(o instanceof VisualizerNode)) return false; VisualizerNode v = (VisualizerNode)o; return v.hashCode == hashCode; } /** String name is taken from the node. */ public String toString () { return displayName; } /** Strong reference. */ private static final class StrongReference extends WeakReference { private Object o; public StrongReference (Object o) { super (null); this.o = o; } public Object get () { return o; } } } /* * Log * 11 Gandalf 1.10 1/12/00 Ian Formanek NOI18N * 10 Gandalf 1.9 1/8/00 Jaroslav Tulach Memory leak fix. * 9 Gandalf 1.8 11/26/99 Patrik Knakal * 8 Gandalf 1.7 11/5/99 Jaroslav Tulach WeakListener has now * registration methods. * 7 Gandalf 1.6 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 6 Gandalf 1.5 9/17/99 Jaroslav Tulach Reorder of nodes works. * 5 Gandalf 1.4 9/17/99 Jaroslav Tulach Change caching. * 4 Gandalf 1.3 9/7/99 Jaroslav Tulach #2445 * 3 Gandalf 1.2 9/1/99 Jaroslav Tulach Holding of children is a * bit stronger. * 2 Gandalf 1.1 8/27/99 Jaroslav Tulach List model can display * more levels at once. * 1 Gandalf 1.0 8/27/99 Jaroslav Tulach * $ */